package top.zlcode.friend.service.user.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import top.zlcode.api.file.RemoteFileService;
import top.zlcode.common.core.constants.CacheConstants;
import top.zlcode.common.core.constants.Constants;
import top.zlcode.common.core.constants.HttpConstants;
import top.zlcode.common.core.context.UserContextHelper;
import top.zlcode.common.core.domain.LoginUser;
import top.zlcode.common.core.domain.R;
import top.zlcode.common.core.domain.vo.LoginUserVO;
import top.zlcode.common.core.enums.ResultCode;
import top.zlcode.common.core.enums.UserIdentity;
import top.zlcode.common.core.enums.UserStatus;
import top.zlcode.common.message.constants.EmailConstants;
import top.zlcode.common.message.enums.EmailTemplate;
import top.zlcode.common.message.service.EmailService;
import top.zlcode.common.redis.service.RedisService;
import top.zlcode.common.security.exception.ServiceException;
import top.zlcode.common.security.service.TokenService;
import top.zlcode.friend.domain.user.User;
import top.zlcode.friend.domain.user.dto.UserDTO;
import top.zlcode.friend.domain.user.dto.UserUpdateDTO;
import top.zlcode.friend.domain.user.vo.UserVO;
import top.zlcode.friend.manager.UserCacheManager;
import top.zlcode.friend.mapper.user.UserMapper;
import top.zlcode.friend.service.file.IFileService;
import top.zlcode.friend.service.user.IUserService;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private EmailService emailService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private UserCacheManager userCacheManager;

    @Autowired
    private IFileService fileService;

    @Autowired
    private RemoteFileService remoteFileService;

    @Value("${zlcode.email.code-expiration:3}") // 从 nacos 读取配置验证码过期时间, 如果没配置默认是 3 分钟
    private Long emailCodeExpiration;

    @Value("${zlcode.email.send-limit:3}") // 每个邮箱每天发送次数限制
    private Integer sendLimit;

    @Value("${zlcode.email.is-send:false}") // 每个邮箱每天发送次数限制
    private Boolean isSend;

    @Value("${jwt.secret:}")
    private String secret;

    @Override
    public boolean sendCode(UserDTO userDTO) {
        if(!checkEmail(userDTO.getEmail())) {
            throw new ServiceException(ResultCode.FAILED_USER_EMAIL);
        }
        String emailCodeKey = getEmailCodeKey(userDTO.getEmail());

        // 判断是否已经发送过验证码
        Long expire = redisService.getExpire(emailCodeKey, TimeUnit.SECONDS);
        if (expire != null && (emailCodeExpiration * 60 - expire) < 60) {
            throw new ServiceException(ResultCode.FAILED_FREQUENT);
        }

        // 限制每天最多可以获取50次验证码
        String codeCountKey = getCodeCountKey(userDTO.getEmail());
        Long sendCount = redisService.getCacheObject(codeCountKey, Long.class);
        if (sendCount != null && sendCount >= sendLimit) {
            throw new ServiceException(ResultCode.FAILED_COUNT_LIMIT);
        }

        String code = isSend ? RandomUtil.randomString(6) : Constants.DEFAULT_EMAIL_CODE;
        String content = EmailTemplate.REG_CODE_TEMPLATE.getContent().formatted(code, emailCodeExpiration);

        if (isSend) {
            boolean result = emailService.send(EmailConstants.ONE_OJ_SUBJECT, content, userDTO.getEmail());
            if (!result) {
                throw new ServiceException(ResultCode.FAILED_SEND_CODE);
            }
        }

        redisService.setCacheObject(emailCodeKey, code, emailCodeExpiration, TimeUnit.MINUTES);
        redisService.increment(codeCountKey); // 使当前邮箱当天收到验证码次数+1
        if (sendCount == null) {
            // 说明是当天第一次获取发送验证码的请求, 并设置 redis 过期时间
            long seconds = ChronoUnit.SECONDS.between(LocalDateTime.now(),
                    LocalDateTime.now().plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0));
            redisService.expire(codeCountKey, seconds, TimeUnit.SECONDS);
        }
        return true;
    }

    @Override
    public String codeLogin(String email, String code) {

        checkCode(email, code);

        User user = baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getEmail, email));

        // 新用户创建账号
        if (user == null) {
            user = new User();
            user.setEmail(email);
            user.setStatus(UserStatus.Normal.getValue());
            baseMapper.insert(user);
        }

        user = baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getEmail, email));

        return tokenService.createToken(user.getUserId(), secret, UserIdentity.ORDINARY.getValue(), user.getNickName());

    }

    @Override
    public boolean logout(String token) {
        if (StrUtil.isNotEmpty(token) && token.startsWith(HttpConstants.PREFIX)) {
            token = token.replaceFirst(HttpConstants.PREFIX, StrUtil.EMPTY);
        }
        return tokenService.deleteLoginUser(token, secret);
    }

    @Override
    public R<LoginUserVO> info(String token) {
        Long userId=  UserContextHelper.getUserId();

        LoginUserVO loginUserVO = new LoginUserVO();
        UserVO userVO = userCacheManager.getUserById(userId);
        loginUserVO.setNickName(userVO.getNickName());
        loginUserVO.setHeadImage(userVO.getHeadImage());
        return R.ok(loginUserVO);
    }

    @Override
    public UserVO detail() {
        Long userId = UserContextHelper.getUserId();
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_UNAUTHORIZED);
        }
        UserVO userVO = userCacheManager.getUserById(userId);
        if (userVO == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        return userVO;
    }

    @Override
    public int edit(UserUpdateDTO userUpdateDTO) {
        User user = getUser();
        user.setNickName(userUpdateDTO.getNickName());
        user.setSex(userUpdateDTO.getSex());
        user.setSchoolName(userUpdateDTO.getSchoolName());
        user.setMajorName(userUpdateDTO.getMajorName());
        user.setPhone(userUpdateDTO.getPhone());
        user.setEmail(userUpdateDTO.getEmail());
        user.setWechat(userUpdateDTO.getWechat());
        user.setIntroduce(userUpdateDTO.getIntroduce());
        String orgHeadImage = user.getHeadImage();
        String remoteImageUrl = fileService.getRemoteFileUrl(user.getHeadImage());
        user.setHeadImage(remoteImageUrl);
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(), remoteImageUrl,
                UserContextHelper.getUserKey());
        user.setHeadImage(orgHeadImage);
        return baseMapper.updateById(user);
    }

    @Override
    public int updateHeadImage(String headImage) {
        User user = getUser();
        String oldUserHeadImage = user.getHeadImage();
        String remoteImageUrl = fileService.getRemoteFileUrl(headImage);
        user.setHeadImage(remoteImageUrl);
        //更新用户缓存
        userCacheManager.refreshUser(user);
        tokenService.refreshLoginUser(user.getNickName(), remoteImageUrl,
                UserContextHelper.getUserKey());
        user.setHeadImage(headImage);
        int res = baseMapper.updateById(user);
        if (res != 1) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        // 删除服务器上的文件
        boolean deleted = fileService.delete(oldUserHeadImage);
        if (!deleted) {
            throw new ServiceException(ResultCode.FILE_DELETE_ERROR);
        }
        return res;
    }

    /**
     * 获取当前登录的用户
     */
    private User getUser() {
        Long userId = UserContextHelper.getUserId();
        if (userId == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        User user = baseMapper.selectById(userId);
        if (user == null) {
            throw new ServiceException(ResultCode.FAILED_USER_NOT_EXISTS);
        }
        return user;
    }

    /**
     * 检验验证码
     * @param email
     * @param code
     */
    private void checkCode(String email, String code) {
        String emailCodeKey = getEmailCodeKey(email);
        String cacheCode = redisService.getCacheObject(emailCodeKey, String.class);
        if (StrUtil.isEmpty(cacheCode)) {
            throw new ServiceException(ResultCode.FAILED_INVALID_CODE);
        }
        if (!cacheCode.equals(code)) {
            throw new ServiceException(ResultCode.FAILED_ERROR_CODE);
        }
        // 登录成功后 删除 redis 中的验证码
        redisService.deleteObject(emailCodeKey);
    }

    private boolean checkEmail(String email) {
        // 邮箱正则表达式，这个表达式可以匹配大部分常见的邮箱格式
        String regex = "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}$";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(email);
        return matcher.matches();
    }

    private String getEmailCodeKey(String email) {
        return CacheConstants.EMAIL_CODE_KEY + email;
    }

    private String getCodeCountKey(String email) {
        return CacheConstants.CODE_COUNT_KEY + email;
    }
}
